/*
 * Niiuzu
 * Copyright (C) 2011  Thierry Nowak
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.niiuzu.client.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.niiuzu.shared.model.FeedChannel;
import com.niiuzu.shared.model.FeedChannelGroup;
import com.niiuzu.shared.model.User;

/**
 * Bulle mémoire disponible du côté client (au sens GWT) stockant les objets
 * nécessaires à l'image d'une session dans une servlet. Il s'agit d'un
 * singleton (une seule instance) afin d'assurer la cohérence des données entre
 * tous les presenters.
 */
public class SessionClient {

	private static SessionClient instance;

	private User user;
	private Map<String, FeedChannelGroup> channelGroupMap;
	private Map<String, FeedChannel> channelMap;

	/**
	 * Construit une nouvelle instance de {@link SessionClient}.
	 */
	private SessionClient() {
		channelGroupMap = new HashMap<String, FeedChannelGroup>();
		channelMap = new HashMap<String, FeedChannel>();
	}

	/**
	 * Récupère l'unique instance de {@link SessionClient}.
	 */
	public static SessionClient getInstance() {
		if (mockedSessionClient != null) {
			return mockedSessionClient;
		}

		if (instance == null) {
			instance = new SessionClient();
		}
		return instance;
	}

	/**
	 * Récupère l'utilisateur authentifié.
	 */
	public User getUser() {
		return user;
	}

	/**
	 * Définit l'utilisateur authentifié.
	 */
	public void setUser(User user) {
		this.user = user;
	}

	/**
	 * {@link Map} contenant les thèmes accessibles dans la session courante.
	 * 
	 * @return {@link Map} dont les clefs sont les identifiants des thèmes (
	 *         {@link FeedChannelGroup#getUid()}).
	 */
	public Map<String, FeedChannelGroup> getChannelGroupMap() {
		return channelGroupMap;
	}

	/**
	 * Ajoute un {@link FeedChannelGroup} dans la session.
	 */
	public void addChannelGroup(FeedChannelGroup channelGroup) {
		channelGroupMap.put(channelGroup.getUid(), channelGroup);
	}

	/**
	 * Suppression d'un thème {@link FeedChannelGroup} de la session.
	 */
	public void removeChannelGroup(FeedChannelGroup channelGroup) {
		channelGroupMap.remove(channelGroup.getUid());
	}

	/**
	 * {@link Map} contenant les flux RSS accessibles dans la session courante.
	 * 
	 * @return {@link Map} dont les clefs sont les identifiants des flux (
	 *         {@link FeedChannel#getUid()}).
	 */
	public Map<String, FeedChannel> getChannelMap() {
		return channelMap;
	}

	/**
	 * Ajoute un flux ({@link FeedChannel}) dans la session.
	 */
	public void addChannel(FeedChannel channel) {
		channelMap.put(channel.getUid(), channel);
	}

	/**
	 * Suppression d'un flux ({@link FeedChannel}) de la session.
	 */
	public void removeChannel(FeedChannel channel) {
		channelMap.remove(channel.getUid());
	}

	/**
	 * Retourne un tableau contenant tous les flux accessibles.
	 */
	public FeedChannel[] getChannelArray() {
		Collection<FeedChannel> channelValues = channelMap.values();
		FeedChannel[] channels = channelValues
				.toArray(new FeedChannel[channelValues.size()]);
		return channels;
	}

	/**
	 * Récupère le thème lié à un flux donné.
	 */
	public FeedChannelGroup getGroupFromChannel(FeedChannel channel) {

		for (FeedChannelGroup channelGroup : channelGroupMap.values()) {
			if (channel.getChannelGroupUid().equals(channelGroup.getUid())) {
				return channelGroup;
			}
		}

		throw new IllegalArgumentException(
				"FeedChannelGroup not found for channel " + channel);
	}

	/**
	 * Récupère les flux liés à un groupe.
	 */
	public FeedChannel[] getChannelsFromGroup(FeedChannelGroup channelGroup) {

		List<FeedChannel> channelList = new ArrayList<FeedChannel>();
		for (FeedChannel channel : channelMap.values()) {
			if (channelGroup.getUid().equals(channel.getChannelGroupUid())) {
				channelList.add(channel);
			}
		}

		FeedChannel[] feedChannels = new FeedChannel[channelList.size()];
		return (FeedChannel[]) channelList.toArray(feedChannels);
	}

	/**
	 * Réinitialise les données sur les flux et les thèmes.
	 */
	public void clearChannels() {
		channelGroupMap.clear();
		channelMap.clear();
	}

	/* ********************************************************************** */
	/* ********************************************************************** */
	/* ********************************************************************** */

	private static SessionClient mockedSessionClient;

	/**
	 * JUnit - Définit un mock pour {@link SessionClient}.
	 */
	public static void setMockedSessionClient(SessionClient mockedSessionClient) {
		SessionClient.mockedSessionClient = mockedSessionClient;
	}

	/**
	 * JUnit - Écrase toutes les données sauvegardées jusqu'ici.
	 */
	static void clearAll() {
		instance = new SessionClient();
	}
}
